﻿' ----------------------------------------------------------------------
'
' FileSystemInfo.vb
'
'   Dbfs.Domain
'
'   Provides the base class for both FileInfo and DirectoryInfo objects.
'
' ----------------------------------------------------------------------
' version:  1.0.0.1
' today:    02/09/06  11:26:00
' ident:    "@(#)/Dbfs/Domain/FileSystemInfo.vb 1.0 0.1 02/09/09 0 rjs"

Imports System.IO
Imports System.Security.Principal
Imports Dbfs.Data
Imports Dbfs.Security


Namespace Dbfs

    ''' <summary>
    ''' (DBFS) Provides the base class for both <see cref="FileInfo"/> and <see cref="DirectoryInfo"/> objects.
    ''' </summary>
    ''' <remarks>This class holds a reference to a an instance of a <see cref="FileSystemEntity"/>.
    ''' The node contains shared copy of a <see cref="FileSystemItemSet"/> that represents the
    ''' history of this node.  History is always read-only.  Only the current item is writable.
    ''' The node also contains a <see cref="FileSystemItem "/> which is an updatable clone 
    ''' of the most recent layer of history.
    ''' </remarks>
    <SerializableAttribute()> _
    Public MustInherit Class FileSystemInfo
        Implements IComparable, IDbfsNode


        Private _connection As DbfsConnection

        ' a dictionary of keyword-value attributes for this specific instance.
        Private _dynamicAttributes As New NodeAttributes

        Private _entity As FileSystemEntity

        ' the original parent object for this instance
        Private _originalParent As FileSystemInfo

        ' the parent info object of this object
        Private _parent As DirectoryInfo


#Region " Fields "
        ''' <summary>
        ''' Represents the fully qualified path of the directory or file.
        ''' </summary>
        ''' <remarks></remarks>
        Protected FullPath As String

        ''' <summary>
        ''' The path originally specified by the user, whether relative or absolute.
        ''' </summary>
        ''' <remarks></remarks>
        Protected OriginalPath As String
#End Region ' Fields

#Region " Constructors "
        ''' <summary>
        ''' Initializes a new instance of the FileSystemInfo class.
        ''' </summary>
        ''' <remarks>Every <see cref="FileSystemInfo"/> object is added to 
        ''' <see cref="Session"/>.  This allows data-mappers to reference
        ''' existing objects when asked for child objects.</remarks>
        Public Sub New()
        End Sub

        Public Sub New(ByVal path As String)
            Me.New()

            ' check for null path
            If path Is Nothing Then
                Throw New ArgumentNullException("path")
            End If

            ' check for valid path characters
            path = path.Trim()
            If path.Length = 0 OrElse _
               path.IndexOfAny(System.IO.Path.GetInvalidPathChars()) > 0 Then
                Throw New ArgumentException("Invalid path.")
            End If

            ' complete the initialization of the object
            Initialize(path)

        End Sub
#End Region ' Constructors

#Region " Properties "
        ''' <summary>
        ''' Gets the access rights for the current user.
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property AccessRights() As System.Security.AccessControl.FileSystemRights
            Get
                Return _entity.AccessRights
            End Get
        End Property

        ''' <summary>
        ''' Gets or sets the attributes for the current file or directory.
        ''' </summary>
        ''' <value><see cref="FileAttributes"/> of the current <see cref="FileSystemInfo"/>.</value>
        ''' <returns></returns>
        ''' <remarks>The value of this property is a combination of the archive, compressed, 
        ''' directory, hidden, offline, read-only, system, and temporary file attribute flags.</remarks>
        Public Property Attributes() As FileAttributes Implements IDbfsNodeItem.Attributes
            Get
                Return _entity.Attributes
            End Get
            Set(ByVal value As FileAttributes)
                _entity.Attributes = value
                Context.Update(_entity)
            End Set
        End Property

        ''' <summary>
        ''' Gets or sets a Date that represents the cancel date of the object.
        ''' The cancel date of the object is the last date the object is available,
        ''' including up through midnight of that date.
        ''' </summary>
        ''' <value>A Date that represents the cancel date of the object.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property CancelDate() As Date? Implements IDbfsNodeItem.CancelDate
            Get
                If _entity.CancelDateUtc Is Nothing Then
                    Return Nothing
                Else
                    Return CType(_entity.CancelDateUtc, DateTime).ToLocalTime
                End If
            End Get
            Set(ByVal value As Date?)
                _entity.CancelDateUtc = CType(value, DateTime).ToUniversalTime
                Context.Update(_entity)
            End Set
        End Property

        ''' <summary>
        ''' Gets or sets a Date that represents the cancel date of the object.
        ''' The cancel date of the object is the last date the object is available,
        ''' including up through midnight of that date.
        ''' </summary>
        ''' <value>A Date that represents the cancel date of the object.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property CancelDateUtc() As Date?
            Get
                Return _entity.CancelDateUtc
            End Get
            Set(ByVal value As Date?)
                _entity.CancelDateUtc = value
                Context.Update(_entity)
            End Set
        End Property

        Public Property Connection() As DbfsConnection
            Get
                Return _connection
            End Get
            Set(ByVal value As DbfsConnection)
                _connection = value
            End Set
        End Property

        Public ReadOnly Property Context() As IDataContext
            Get
                Return _connection.Context
            End Get
        End Property

        ''' <summary>
        ''' Gets or sets the creation time of the current FileSystemInfo object.
        ''' </summary>
        ''' <value>The creation date and time of the current FileSystemInfo object.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property CreationTime() As DateTime Implements IDbfsNodeItem.CreationTime
            Get
                Return _entity.CreationTimeUtc.ToLocalTime
            End Get
            Set(ByVal value As Date)
                _entity.CreationTimeUtc = value.ToUniversalTime
                Context.Update(_entity)
            End Set
        End Property

        ''' <summary>
        ''' Gets or sets the time, in coordinated universal time (UTC), when the 
        ''' current file or directory was created.
        ''' </summary>
        ''' <value>The UTC time when the current file was created.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property CreationTimeUtc() As DateTime
            Get
                Return _entity.CreationTimeUtc
            End Get
            Set(ByVal value As Date)
                _entity.CreationTimeUtc = value
                Context.Update(_entity)
            End Set
        End Property

        Public ReadOnly Property DeleteTime() As Date?
            Get
                If _entity.DeleteTimeUtc Is Nothing Then
                    Return Nothing
                Else
                    Return CType(_entity.DeleteTimeUtc, DateTime).ToLocalTime
                End If
            End Get
        End Property

        ''' <summary>
        ''' Gets a Date that represents the time when this record became invalid.
        ''' </summary>
        ''' <value>A Date indicating when this record was effectively deleted.</value>
        ''' <returns></returns>
        ''' <remarks>DeleteDate will only be populated if object changes are being tracked.</remarks>
        Public ReadOnly Property DeleteTimeUtc() As Date?
            Get
                Return _entity.DeleteTimeUtc
            End Get
        End Property

        ''' <summary>
        ''' Gets the dynamic attributes for the current file or directory.  The dynamic attributes
        ''' is a dictionary of string key-value pairs that exist only for the duration of the
        ''' current instance of the containing object.
        ''' </summary>
        ''' <value><see cref="NodeAttributes"/> of the current <see cref="FileSystemInfo"/>.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property DynamicAttributes() As NodeAttributes
            Get
                Return _dynamicAttributes
            End Get
        End Property

        ''' <summary>
        ''' Gets or sets a Date that represents the effective date of the object.
        ''' The effective date of the object is the date when it is first available.
        ''' </summary>
        ''' <value>A Date that represents the effective date of the object.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property EffectiveDate() As DateTime Implements IDbfsNodeItem.EffectiveDate
            Get
                Return _entity.EffectiveDateUtc.ToLocalTime
            End Get
            Set(ByVal value As Date)
                _entity.EffectiveDateUtc = value.Date.ToUniversalTime
                Context.Update(_entity)
            End Set
        End Property

        ''' <summary>
        ''' Gets or sets a Date that represents the effective date of the object.
        ''' The effective date of the object is the date when it is first available.
        ''' </summary>
        ''' <value>A Date that represents the effective date of the object.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property EffectiveDateUtc() As DateTime
            Get
                Return _entity.EffectiveDateUtc
            End Get
            Set(ByVal value As Date)
                _entity.EffectiveDateUtc = value.Date
                Context.Update(_entity)
            End Set
        End Property

        Public Overridable ReadOnly Property EffectivePath() As String
            Get
                'Return _entity.EffectivePath
                Dim nodePath As String = Name
                Dim firstChar As Char = nodePath.Substring(0, 1)
                If Not (firstChar = DIRECTORY_SEP_CHAR OrElse firstChar = ALT_DIRECTORY_SEP_CHAR) Then
                    nodePath = DIRECTORY_SEP_CHAR & nodePath
                End If
                If _parent Is Nothing Then
                    Return nodePath
                Else
                    Dim parentEffectivePath As String = _parent.EffectivePath
                    If parentEffectivePath = DIRECTORY_SEP_CHAR OrElse parentEffectivePath = ALT_DIRECTORY_SEP_CHAR Then
                        Return nodePath
                    End If
                    Return parentEffectivePath & nodePath
                End If
            End Get
        End Property

        Protected Friend Property Entity() As FileSystemEntity
            Get
                Return _entity
            End Get
            Set(ByVal value As FileSystemEntity)
                _entity = value
            End Set
        End Property


        ''' <summary>
        ''' Gets a value indicating whether the file or directory exists.
        ''' </summary>
        ''' <value><b>true</b> if the file or directory exists; otherwise, <b>false</b>.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Exists() As Boolean Implements IDbfsNodeItem.Exists
            Get
                ' TODO: should Exists be removed?
                Return FileSystemItem.Exists
            End Get
        End Property

        ''' <summary>
        ''' Gets the extended attributes for the current file or directory.  The extended attributes
        ''' is a dictionary of string key-value pairs that are defined for the node that this
        ''' object points to.
        ''' </summary>
        ''' <value><see cref="NodeAttributes"/> of the current <see cref="FileSystemInfo"/>.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ExtendedAttributes() As Dictionary(Of String, String) Implements IDbfsNode.ExtendedAttributes
            Get
                Return _entity.ExtendedAttributes
            End Get
        End Property

        ''' <summary>
        ''' Gets the string representing the extension part of the file.
        ''' </summary>
        ''' <value>A String containing the <see cref="FileSystemInfo">FileSystemInfo</see>
        ''' extension.</value>
        ''' <returns></returns>
        ''' <remarks>The Extension property returns the FileSystemInfo extension,
        ''' including the period (.).  For example, for a file NewFile.txt, this
        ''' property returns ".txt".</remarks>
        Public ReadOnly Property Extension() As String Implements IDbfsNodeItem.Extension
            Get
                Return Path.GetExtension(Name)
            End Get
        End Property

        ''' <summary>
        ''' Refers to the current <see cref="FileSystemItem"/> based on the selected
        ''' view date of the object model.  FileSystemItem is a reference to
        ''' an entry in the history of this node.
        ''' </summary>
        ''' <remarks></remarks>
        Protected Friend Overridable Property FileSystemItem() As FileSystemItem
            Get
                Return _entity.FileSystemItem
            End Get
            Set(ByVal value As FileSystemItem)
                _entity.FileSystemItem = value
            End Set
        End Property

        ''' <summary>
        ''' Gets the full path of the directory or file.
        ''' </summary>
        ''' <value>A string containing the full path.</value>
        ''' <returns></returns>
        ''' <remarks>For example, for a file \NewFile.txt, this property
        ''' returns "\NewFile.txt".</remarks>
        Public Overridable ReadOnly Property FullName() As String Implements IDbfsNodeItem.FullName
            Get
                Return FullPath
            End Get
        End Property

        ''' <summary>
        ''' Returns the collection of <see cref="FileSystemItem"/>s that represent the
        ''' history of this node.
        ''' </summary>
        ''' <value>A <see cref="FileSystemItemSet"/> the holds a collection of 
        ''' <see cref="FileSystemItem"/>.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Protected ReadOnly Property History() As FileSystemInfo
            Get
                'Return _entity.History
            End Get
        End Property

        Public ReadOnly Property Id() As Integer
            Get
                Return _entity.Id
            End Get
        End Property

        ''' <summary>
        ''' Gets or sets the time the current file or directory was last accessed.
        ''' </summary>
        ''' <value>The time that the current file or directory was last accessed.</value>
        ''' <returns></returns>
        ''' <remarks>If the file described in the FileSystemInfo object does not
        ''' exist, this property will return 12:00 midnight, January 1, 1601 A.D.
        ''' (C.E.) Coordinated Universal Time (UTC), adjusted to local time.</remarks>
        Public Property LastAccessTime() As DateTime Implements IDbfsNodeItem.LastAccessTime
            Get
                Return _entity.LastAccessTimeUtc.ToLocalTime
            End Get
            Set(ByVal value As DateTime)
                _entity.LastAccessTimeUtc = value.ToUniversalTime
                Context.Update(_entity)
            End Set
        End Property

        ''' <summary>
        ''' Gets or sets the time, in coordinated universal time (UTC), that the current file 
        ''' or directory was last accessed.
        ''' </summary>
        ''' <value>The UTC time that the current file or directory was last accessed.</value>
        ''' <returns></returns>
        ''' <remarks>If the file described in the FileSystemInfo object does not
        ''' exist, this property will return 12:00 midnight, January 1, 1601 A.D.
        ''' (C.E.) Coordinated Universal Time (UTC), adjusted to local time.</remarks>
        Public Property LastAccessTimeUtc() As DateTime
            Get
                Return _entity.LastAccessTimeUtc
            End Get
            Set(ByVal value As DateTime)
                _entity.LastAccessTimeUtc = value
                Context.Update(_entity)
            End Set
        End Property

        ''' <summary>
        ''' Gets or sets the time when the current file or directory was last written to.
        ''' </summary>
        ''' <value>The time the current file was last written.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property LastWriteTime() As DateTime Implements IDbfsNodeItem.LastWriteTime
            Get
                Return _entity.LastWriteTimeUtc.ToLocalTime
            End Get
            Set(ByVal value As DateTime)
                _entity.LastWriteTimeUtc = value.ToUniversalTime
                Context.Update(_entity)
            End Set
        End Property

        ''' <summary>
        ''' Gets or sets the time, in coordinated universal time (UTC), when the 
        ''' current file or directory was last written to.
        ''' </summary>
        ''' <value>The UTC time when the current file was last written to.</value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property LastWriteTimeUtc() As DateTime
            Get
                Return _entity.LastWriteTimeUtc
            End Get
            Set(ByVal value As DateTime)
                _entity.LastWriteTimeUtc = value
                Context.Update(_entity)
            End Set
        End Property

        ''' <summary>
        ''' For files, gets the name of the file. For directories, gets the name of the 
        ''' last directory in the hierarchy if a hierarchy exists. Otherwise, the Name 
        ''' property gets the name of the directory.
        ''' </summary>
        ''' <value>A string that is the name of the parent directory, the name of the 
        ''' last directory in the hierarchy, or the name of a file, including the file 
        ''' name extension.</value>
        ''' <returns></returns>
        ''' <remarks>For a directory, Name returns only the name of the parent directory, 
        ''' such as Dir, not c:\Dir. For a subdirectory, Name returns only the name of the 
        ''' subdirectory, such as Sub1, not c:\Dir\Sub1. <para/>For a file, Name returns 
        ''' only the file name and file name extension, such as MyFile.txt, 
        ''' not c:\Dir\Myfile.txt</remarks>
        Public MustOverride ReadOnly Property Name() As String Implements IDbfsNodeItem.Name

        ''' <summary>
        ''' Gets the path originally specified by the user, whether relative or absolute.
        ''' </summary>
        ''' <value>A string containing the original path.</value>
        ''' <returns></returns>
        ''' <remarks>For example, for a file \NewFile.txt, this property
        ''' returns "\NewFile.txt".</remarks>
        Public Overridable ReadOnly Property OriginalName() As String Implements IDbfsNodeItem.OriginalName
            Get
                Return OriginalPath
            End Get
        End Property

        ''' <summary>
        ''' Gets or sets the parent directory of a specified file system object.
        ''' </summary>
        ''' <value>The parent directory, or a null reference (<b>Nothing</b>
        ''' in Visual Basic) if the path is null or if the file path denotes
        ''' a root (such as "\", "C:", or * "\\server\share").</value>
        ''' <returns></returns>
        ''' <remarks>Although normally implemented only for objects that act as
        ''' directories, this property is made public for all filesystem objects
        ''' in DBFS to support the IHierarchyData interface.</remarks>
        Protected Friend Overridable Property ParentInfo() As DirectoryInfo
            Get
                If _parent Is Nothing Then
                    Dim parentPath As String = Path.GetDirectoryName(FullPath)
                    If _entity.ParentEntity IsNot Nothing Then
                        _parent = Session.Get(parentPath)
                        If _parent Is Nothing Then _
                            _parent = New DirectoryInfo(parentPath)
                    End If
                End If
                Return _parent
            End Get
            Set(ByVal value As DirectoryInfo)
                If _originalParent Is Nothing Then _
                    _originalParent = _parent
                If value IsNot Nothing Then
                    If value.FullPath.EndsWith(DIRECTORY_SEP_CHAR) Then
                        ' TODO: this might be used in the treeview somewhere
                        '    FullPath = value.FullPath & Name
                    Else
                        '    FullPath = value.FullPath & DIRECTORY_SEP_CHAR & Name
                    End If
                End If
                _parent = value
            End Set
        End Property

        Friend Overridable Property ParentNode() As FileSystemEntity
            Get
                Return _entity.ParentEntity
            End Get
            Set(ByVal value As FileSystemEntity)
                _entity.ParentEntity = value
            End Set
        End Property

        ''' <summary>
        ''' Applies access control list (ACL) entries described by a <see cref="FileSystemSecurity"/> 
        ''' object to the directory described by the current <see cref="DirectoryInfo"/> object.
        ''' </summary>
        ''' <param name="fileSystemSecurity">A <see cref="FileSystemSecurity"/> object that describes 
        ''' an ACL entry to apply to this directory.</param>
        ''' <remarks></remarks>
        Public MustOverride Sub SetAccessControl(ByVal fileSystemSecurity As System.Security.AccessControl.FileSystemSecurity)

        ''' <summary>
        ''' Applies access control list (ACL) entries described by a <see cref="FileSystemSecurity"/> 
        ''' object to the directory described by the current <see cref="DirectoryInfo"/> object.
        ''' </summary>
        ''' <param name="fileSystemSecurity">A <see cref="FileSystemSecurity"/> object that describes 
        ''' an ACL entry to apply to this directory.</param>
        ''' <remarks></remarks>
        Public Overridable Sub SetAccessControl(ByVal fileSystemSecurity As Security.FileSystemSecurity)
            ' apply security, check to see if we can change the access control list
            If (AccessRights And System.Security.AccessControl.FileSystemRights.ChangePermissions) = 0 Then
                Throw New DbfsException("Access Denied")
            End If
            If fileSystemSecurity IsNot _entity.FileSystemSecurity Then
                Dim ar As AccessRule
                For Each ar In fileSystemSecurity.GetAccessRules(True, False, GetType(NTAccount))
                    _entity.FileSystemSecurity.RemoveAccessRule(ar)
                Next
                For Each ar In fileSystemSecurity.GetAccessRules(True, False, GetType(NTAccount))
                    _entity.FileSystemSecurity.AddAccessRule(ar)
                Next
            End If
            ' need to clear the cached access rights when the ACL is changed
            _entity.ClearAccessRights()
            ' TODO: all of this does not get the security persisted
        End Sub

        ''' <summary>
        ''' Gets the short 8.3 version of the name for this folder.
        ''' </summary>
        ''' <value></value>
        ''' <returns>A String containing the 8.3 name for this folder.</returns>
        ''' <remarks></remarks>
        Public MustOverride ReadOnly Property ShortName() As String Implements IDbfsNodeItem.ShortName

        ''' <summary>
        ''' Gets or sets the Track-changes flag for this folder.
        ''' </summary>
        ''' <value>A Boolean value indicating if changes to this folder are tracked.</value>
        ''' <returns></returns>
        ''' <remarks>Transactions should always be used when <b>TrackChanges</b> is enabled
        ''' to reduce the number of records written to the database.</remarks>
        Public Property TrackChanges() As Boolean Implements IDbfsNodeItem.TrackChanges
            Get
                Return _entity.TrackChanges
            End Get
            Set(ByVal value As Boolean)
                _entity.TrackChanges = value
                Context.Update(_entity)
            End Set
        End Property

        Public Property ViewDate() As Date
            Get
                Return _entity.ViewDate
            End Get
            Set(ByVal value As Date)
                _entity.ViewDate = value
            End Set
        End Property
#End Region ' Properties

#Region " Methods "
        Public Function Clone() As FileSystemInfo
            Dim fsi As FileSystemInfo
            fsi = MemberwiseClone()
            'fsi.Entity = Entity.Clone
            'fsi.History = New Collection(Of FileSystemItem)(History)
            'fsi._nodeAttributes = NodeAttributes.Copy
            Return fsi
        End Function

        ''' <summary>
        ''' Deletes a file or directory.
        ''' </summary>
        ''' <remarks></remarks>
        Public MustOverride Sub Delete()

        ''' <summary>
        ''' Gets a <see cref="FileSystemSecurity"/> object that encapsulates the access control list 
        ''' (ACL) entries for the file described by the current <see cref="FileSystemInfo"/> object.
        ''' </summary>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public MustOverride Function GetAccessControl() As Security.FileSystemSecurity

        Protected Friend Sub Initialize(ByVal path As String)

            ' local variables
            Dim fileSystemName As String

            ' initialize variables
            fileSystemName = String.Empty

            ' the OriginalPath is whatever is specified by the user
            OriginalPath = path

            ' the FullPath is initially the same as OriginalPath
            FullPath = path

            ' the dbfs instance to connect to is given by the high level
            ' qualifier in the path using the UNC convention
            If path.StartsWith(UNC_CHARS) Then
                path = path.Substring(2)
                Dim i = path.IndexOfAny(DIRECTORY_SEP_CHARS)
                If i > 0 Then
                    fileSystemName = path.Substring(0, i)
                Else
                    fileSystemName = path
                End If
            End If

            ' establish the database connection
            _connection = New DbfsConnection(fileSystemName)

        End Sub

        ''' <summary>
        ''' Refreshes the state of the object.
        ''' </summary>
        ''' <remarks><b>FileSystemInfo.Refresh</b> takes a snapshot of the file from the current file system.</remarks>
        Public MustOverride Sub Refresh()

        ''' <summary>
        ''' Changes the name of this object to the specified value.
        ''' </summary>
        ''' <param name="value">A String specifying a new name for this object.</param>
        ''' <remarks></remarks>
        Protected Sub SetName(ByVal value As String)
            FileSystemItem.Name.Value = value
            Context.Update(Entity)
        End Sub

        Protected Friend Overridable Sub Unmount()
            If _originalParent IsNot Nothing Then
                ParentInfo = _originalParent
            End If
        End Sub
#End Region ' Methods

#Region " IComparable Support "
        ''' <summary>
        ''' Compares the current instance with another object of the same type and returns an integer 
        ''' that indicates whether the current instance precedes, follows, or occurs in the same 
        ''' position in the sort order as the other object.
        ''' </summary>
        ''' <param name="obj">An object to compare with this instance.</param>
        ''' <returns>A 32-bit signed integer that indicates the relative order of the objects being compared.</returns>
        ''' <remarks></remarks>
        Public Overloads Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
            If TypeOf obj Is FileSystemInfo Then
                Dim otherFsInfo As FileSystemInfo = DirectCast(obj, FileSystemInfo)
                If (Me.Attributes And FileAttributes.Directory) Then
                    If (otherFsInfo.Attributes And FileAttributes.Directory) Then
                        Return Me.Name.CompareTo(otherFsInfo.Name)
                    Else
                        Return -1
                    End If
                Else
                    ' if the current instance is NOT a directory...
                    If (otherFsInfo.Attributes And FileAttributes.Directory) Then
                        Return 1
                    Else
                        Return Me.Name.CompareTo(otherFsInfo.Name)
                    End If
                End If
            Else
                Throw New ArgumentException("Object is not a FileSystemInfo.")
            End If
        End Function
#End Region ' IComparable Support

    End Class

End Namespace